package org.sgx.gwteditors.client.impl1.complex;

import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.sgx.gwteditors.client.editor.Editor;
import org.sgx.gwteditors.client.editor.EditorNotFound;
import org.sgx.gwteditors.client.editor.Types;
import org.sgx.gwteditors.client.editor.error.EditorError;
import org.sgx.gwteditors.client.editor.error.EditorErrorManager;
import org.sgx.gwteditors.client.editor.event.EditorEventManager;
import org.sgx.gwteditors.client.editor.event.EditorValueChangeEvent;
import org.sgx.gwteditors.client.editor.event.ValueChangeListener;
import org.sgx.gwteditors.client.impl1.EditorFramework1;
import org.sgx.gwteditors.client.impl1.EditorManager1;
import org.sgx.gwteditors.client.impl1.ui.res.EditorImpl1Resources;

import com.google.gwt.dom.client.Style.Cursor;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Widget;

/**
 * matrix 2d editor, all members must be same type. This editor is divided in two components, first a simple string list on the left, and a PH editor on the right. When user clicks
 * an item on the list, the right PH editor will be loaded. Also the list supporting adding, deleting and moving the items.
 * 
 * @author sg
 */
public class Array1DEditor3<T> extends FlexTable implements Editor<List<T>> {

	public static interface Namer<T> {
		String getName(T t);
	}

	public static interface ItemCreator<T> {
		T createNewItem();
	}

	static Logger logger = Logger.getLogger(Array1DEditor3.class + "");
	List<Editor<T>> eds = null;
	private List<T> model;
	private String type;
	private Namer<T> namer;
	List<Anchor> anchors;
	private ItemCreator<T> itemCreator;
	public Array1DEditor3(ItemCreator<T> itemCreator) {
		this(null, null,itemCreator ); 
	}
	public Array1DEditor3(String type, Namer<T> namer, ItemCreator<T> itemCreator) {
		super();
		eds = new LinkedList<Editor<T>>();
		this.type = type == null ? EditorFramework1.TYPE_PROPERTYHAVER : type;
		this.namer = namer == null ? new Namer<T>() {			
			@Override
			public String getName(T t) {
				return t==null?"null":t.toString();
			}
		} : namer;
		anchors = new LinkedList<Anchor>();
		this.itemCreator = itemCreator;
	}
	public Array1DEditor3(Namer<T> namer, ItemCreator<T> itemCreator) {
		this(null, namer, itemCreator); 
	}
	private FlexTable itemsTable;
	private FlexTable itemList;
	private int selectedIndex = -1;
	protected Editor<T> currentEditor = null;

	boolean guiinit=false;
	private void initGUI() {
		if(guiinit)
			return;
		guiinit=true;
		itemsTable = new FlexTable();
		setWidget(0, 0, itemsTable);

		HorizontalPanel itemlistButtonPanel = new HorizontalPanel();
		itemlistButtonPanel.setSpacing(4);
		itemsTable.setWidget(0, 0, itemlistButtonPanel);
		itemlistButtonPanel.setHeight("100%");

		Image addButton = new Image(EditorImpl1Resources.instance.imageAdd16());
		addButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				doAddNewItem();
			}
		});
		addButton.setTitle("Add a new item at the end");
		addButton.getElement().getStyle().setCursor(Cursor.POINTER);

		itemlistButtonPanel.add(addButton);

		Image deleteButton = new Image(EditorImpl1Resources.instance.imageRemove16());
		deleteButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				doDeleteItem();
			}
		});
		itemlistButtonPanel.add(deleteButton);
		deleteButton.setTitle("Delete selected item");
		deleteButton.getElement().getStyle().setCursor(Cursor.POINTER);

		Image upButton = new Image(EditorImpl1Resources.instance.imageUpArrow16());
		upButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				doMoveItemUp();
			}
		});
		itemlistButtonPanel.add(upButton);
		upButton.setTitle("Move selected item up");
		upButton.getElement().getStyle().setCursor(Cursor.POINTER);

		Image downButton = new Image(EditorImpl1Resources.instance.imageDownArrow16());
		downButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				doMoveItemDown();
			}
		});
		itemlistButtonPanel.add(downButton);
		downButton.setTitle("Move selected item down");
		downButton.getElement().getStyle().setCursor(Cursor.POINTER);

		itemList = new FlexTable();
		itemsTable.setWidget(1, 0, itemList);
		itemList.setSize("100%", "100%");
		getCellFormatter().setVerticalAlignment(0, 0, HasVerticalAlignment.ALIGN_TOP);
	}

	protected void doMoveItemDown() {
		if (selectedIndex == -1)
			Window.alert("You must first select some item");
		else if (selectedIndex < anchors.size() - 1) {
			Anchor anchor = anchors.get(selectedIndex);
			Anchor nextAnchor = anchors.get(selectedIndex + 1);
			itemList.setWidget(selectedIndex + 1, 0, anchor);
			itemList.setWidget(selectedIndex, 0, nextAnchor);
			anchors.set(selectedIndex, nextAnchor);
			anchors.set(selectedIndex + 1, anchor);

			T selModel = model.get(selectedIndex), 
					nextModel = model.get(selectedIndex + 1);
			model.set(selectedIndex + 1, selModel);
			model.set(selectedIndex, nextModel);
			
			Editor<T> selEd = eds.get(selectedIndex),
					nextEd = eds.get(selectedIndex + 1);

			eds.set(selectedIndex, nextEd);
			eds.set(selectedIndex + 1, selEd);
			
			selectedIndex++;
			doSetSelected(selectedIndex); 
		}
	}

	protected void doMoveItemUp() {
		if (selectedIndex == -1)
			Window.alert("You must first select some item");
		else if (selectedIndex > 0) {
			Anchor anchor = anchors.get(selectedIndex);
			Anchor prevAnchor = anchors.get(selectedIndex - 1);
			itemList.setWidget(selectedIndex - 1, 0, anchor);
			itemList.setWidget(selectedIndex, 0, prevAnchor);
			anchors.set(selectedIndex, prevAnchor);
			anchors.set(selectedIndex - 1, anchor);

			T selModel = model.get(selectedIndex), prevModel = model.get(selectedIndex - 1);
			model.set(selectedIndex - 1, selModel);
			model.set(selectedIndex, prevModel);

			Editor<T> selEd = eds.get(selectedIndex), prevEd = eds.get(selectedIndex - 1);
			eds.set(selectedIndex, prevEd);
			eds.set(selectedIndex - 1, selEd);

			selectedIndex--;
			// TODO: fire change event
		}
	}

	protected void doDeleteItem() {
		if (selectedIndex == -1)
			Window.alert("You must first select some item");
		else {
			anchors.remove(selectedIndex);
			int i = selectedIndex + 1;
			while (i < anchors.size()) {
				Editor<T> ed = eds.get(i);
				if (ed == null)
					break;
				eds.set(i - 1, ed);
				itemList.removeCell(i - 1, 0);
				itemList.setWidget(i - 1, 0, anchors.get(i));
				i++;
			}
			itemList.removeCell(i - 1, 0);
			removeCell(0, 1);

			// TODO: fire change event¡¡¡¡¡¡¡¡¡¡¡
		}
	}

	protected void doAddNewItem() {
		// TODO add after the selected item
		T newItem = itemCreator.createNewItem();
		// System.out.println("new item: "+newItem);
		model.add(newItem);
		Anchor anchor = newAnchorFor(newItem);

		itemList.setWidget(model.size() - 1, 0, anchor);
		doSetSelected(model.size() - 1); 
		// Editor ed = Types.getInstance().newEditor(type);
	}

	@Override
	public void addValueChangeListener(ValueChangeListener<List<T>> l) {
		EditorEventManager.<List<T>> getInstance().addValueChangeListener(this, l);
	}

	@Override
	public void removeValueChangeListener(ValueChangeListener<List<T>> l) {
		EditorEventManager.<List<T>> getInstance().addValueChangeListener(this, l);
	}

	@Override
	public List<EditorError<List<T>>> getErrors() {
		return EditorErrorManager.<List<T>> getInstance().getErrors(this);
	}

	@Override
	public List<T> flush() {
		List<T> l = new LinkedList<T>(); 
//		System.out.println("AAA flush "+eds.size());
		for (int i = 0; i < eds.size(); i++) {
			Editor<T> ed = _getEditorForIndex(i); 
			l.add(ed.flush()); 
		}
		return l;
	}

	@Override
	public void load(List<T> model) {
		initGUI();
		if (model == null)
			return;

		this.model = model;
		// for (int i = 0; i < model.length; i++) {
		for (int i = 0; i < model.size(); i++) {
			T o = model.get(i);// model[i];
			if (o == null)
				continue;

			Anchor anchor = newAnchorFor(o);

			itemList.setWidget(i, 0, anchor);
			
			eds.add(i,null);
		}
	}

	private Anchor newAnchorFor(T o) {
		Anchor anchor = new Anchor(namer.getName(o));
		anchors.add(anchor);

		anchor.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				int index = anchors.indexOf(event.getSource());
				doSetSelected(index);

				// we only create editors when required.

				Editor<T> ed = _getEditorForIndex(index);
				if (ed == null)
					return;

				if (currentEditor != null) {
					try {// sometimes it is already empty so we cannot remove w/o exception
						Array1DEditor3.this.removeCell(0, 1);
						currentEditor.getWidget().removeFromParent();
					} catch (Exception e) {
					}
				}

				currentEditor = ed;

				ed.load(_getModel().get(index));
				Array1DEditor3.this.setWidget(0, 1, ed.getWidget());

				// final Widget w = ed.getWidget();

				// TODO: subeditors events.

				// ed.addValueChangeListener(new ValueChangeListener<T>() {
				// @Override
				// public void notifyValueChange(EditorValueChangeEvent<T> evt) {
				// // System.out.println("notifyValueChange : "+index+", "+
				// // anchors.get(index)+", namer: "+namer+", val: "+evt.getSrc().flush());
				// anchors.get(index).setText(namer.getName(evt.getValue()));
				//
				// }
				// });
				event.preventDefault();
				// System.out.println("clicked: "+o+" - name: "+name);

				// TODO: fire events
			}
		});

		return anchor;
	}

	protected Editor<T> _getEditorForIndex(final int index) {
		Editor<T>ed = eds.get(index);

		if (ed==null) {
			try {
				ed = Types.getInstance().newEditor(type);// EditorFactoryImpl.<Object>getInstance().getEditorForObject(o);
				eds.remove(index); 
				eds.add(index, ed);
				ed.load(model.get(index)); 
				ed.addValueChangeListener(new ValueChangeListener<T>() {
					@Override
					public void notifyValueChange(EditorValueChangeEvent<T> evt) {
						String name = namer.getName(evt.getSrc().flush()); 
						anchors.get(index).setText(name); 
					}
				}); 
			} catch (EditorNotFound e) {
				T m = _getModel().get(index);
				logger.log(Level.WARNING, " exception building editor for field " + index + ", class: " + m
						+ " : " + m + ". Cause: " + e.getCause() + " - " + e.getMessage());
				e.printStackTrace();
			}
		} 
		return ed;

	}
	protected List<T> _getModel() {
		return model;
	}

	protected void doSetSelected(int index) {
		selectedIndex = index;
		for (Anchor a : anchors) {
			a.getElement().getStyle().setProperty("borderBottom", "0");
			a.getElement().getStyle().setProperty("backgroundColor", "transparent");
		}
		Element el = anchors.get(index).getElement();
		el.getStyle().setProperty("borderBottom", "1px solid #333333");
		el.getStyle().setProperty("backgroundColor", "#ededed");
	}

	@Override
	public Widget getWidget() {
		return this;
	}

	@Override
	public boolean isSimple() {
		return false;
	}

	@Override
	public String getEditorId() {
		return EditorManager1.getInstance().getEditorName(this);
	}

	@Override
	public String[] getEditorTypesSupported() {
		return null;
	}

	public Namer<T> getNamer() {
		return namer;
	}

	public void setNamer(Namer<T> namer) {
		this.namer = namer;
	}

	public int getSelectedIndex() {
		return selectedIndex;
	}

	public ItemCreator<T> getItemCreator() {
		return itemCreator;
	}

	public void setItemCreator(ItemCreator<T> itemCreator) {
		this.itemCreator = itemCreator;
	}
	
	
}
